# -*- coding: utf-8 -*-

# tests/test_writer.py
# Part of ‘manpage’, a Python library for making Unix manual documents.
#
# Copyright © 2015–2016 Ben Finney <ben+python@benfinney.id.au>
#
# This is free software: see the grant of license at end of this file.

""" Unit tests for manual page writer behaviour. """

import sys
import os
import os.path
import builtins
import io
import tempfile
import unittest
import unittest.mock

__package__ = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
__import__(__package__)
sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import manpage.document
from .test_document import (
        setup_metadata_instance,
        )


def setup_document_instance(testcase, test_class=None):
    """ Set up a test `Document` instance. """
    if test_class is None:
        test_class = testcase.document_class
    setup_metadata_instance(testcase)
    testcase.test_document_args = {
            'metadata': testcase.test_metadata_instance,
            }

    testcase.test_document_instance = test_class(
            **testcase.test_document_args)


def make_fake_filesystem_path():
    """ Make an unpredictable fake filesystem path. """
    path = tempfile.mktemp()
    return path


class Writer_TestCase(unittest.TestCase):
    """ Test cases for class `Writer`. """

    writer_class = manpage.document.Writer
    document_class = manpage.document.Document

    scenarios = [
            ('simple', {
                'test_path': make_fake_filesystem_path(),
                }),
            ('simple', {
                'test_path': make_fake_filesystem_path(),
                'test_encoding': "lorem",
                }),
            ]

    def setUp(self):
        """ Set up test fixtures. """
        super().setUp()

        setup_document_instance(self)

    def make_test_args(self, scenario):
        """ Make the test arguments for the test scenario. """
        test_args = {
                'document': self.test_document_instance,
                'path': scenario['test_path'],
                }
        if 'test_encoding' in scenario:
            test_args['encoding'] = scenario['test_encoding']

        return test_args

    def test_instantiate(self):
        """ An instance should be created. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                test_args = self.make_test_args(scenario)
                instance = manpage.document.Writer(**test_args)
                self.assertIsInstance(instance, self.writer_class)

    def test_has_specified_document(self):
        """ Should have specified `document` value. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                test_args = self.make_test_args(scenario)
                instance = manpage.document.Writer(**test_args)
                self.assertEqual(
                        instance.document, self.test_document_instance)

    def test_has_specified_path(self):
        """ Should have specified `path` value. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                test_args = self.make_test_args(scenario)
                instance = manpage.document.Writer(**test_args)
                self.assertEqual(
                        instance.path, scenario['test_path'])

    def test_has_specified_encoding(self):
        """ Should have specified `encoding` value. """
        for (scenario_name, scenario) in (
                (scenario_name, scenario)
                for (scenario_name, scenario) in self.scenarios
                if 'test_encoding' in scenario):
            with self.subTest(scenario=scenario_name):
                test_args = self.make_test_args(scenario)
                instance = manpage.document.Writer(**test_args)
                self.assertEqual(
                        instance.encoding, scenario['test_encoding'])

    def test_has_default_encoding(self):
        """ Should have default `encoding` value. """
        for (scenario_name, scenario) in (
                (scenario_name, scenario)
                for (scenario_name, scenario) in self.scenarios
                if 'test_encoding' not in scenario):
            with self.subTest(scenario=scenario_name):
                test_args = self.make_test_args(scenario)
                instance = manpage.document.Writer(**test_args)
                expected_encoding = "utf-8"
                self.assertEqual(instance.encoding, expected_encoding)


class Writer_write_TestCase(unittest.TestCase):
    """ Test cases for method `Writer.write`. """

    writer_class = manpage.document.Writer
    document_class = manpage.document.Document

    def setUp(self):
        """ Set up test fixtures. """
        super().setUp()

        setup_document_instance(self)

        self.test_path = make_fake_filesystem_path()
        self.test_encoding = "lorem"

        self.patch_builtin_open()
        self.patch_document_as_markup()

    def patch_builtin_open(self):
        """ Patch `builtins.open` for this test case. """
        orig_open = builtins.open

        self.fake_file = unittest.mock.MagicMock()
        self.fake_file.__enter__.return_value = self.fake_file

        def fake_open(path, *args, **kwargs):
            if path == self.test_path:
                result = self.fake_file
            else:
                result = orig_open(path, *args, **kwargs)
            return result

        mock_open = unittest.mock.mock_open()
        mock_open.side_effect = fake_open

        func_patcher = unittest.mock.patch.object(
                builtins, "open", new=mock_open)
        func_patcher.start()
        self.addCleanup(func_patcher.stop)

    def patch_document_as_markup(self):
        """ Patch the document `as_markup` method for this test case. """
        self.fake_document_markup = "Lorem ipsum"

        func_patcher = unittest.mock.patch.object(
                self.test_document_instance, "as_markup",
                return_value=self.fake_document_markup)
        func_patcher.start()
        self.addCleanup(func_patcher.stop)

    def test_writes_expected_content_to_expected_file(self):
        """ Should call `write` on expected file with expected content. """
        instance = manpage.document.Writer(
                self.test_document_instance, self.test_path,
                encoding=self.test_encoding)
        instance.write()
        self.fake_file.write.assert_called_with(self.fake_document_markup)


# This is free software: you may copy, modify, and/or distribute this work
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 3 of that license or any later version.
#
# No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details,
# or view it online at <URL:https://www.gnu.org/licenses/gpl-3.0.html>.


# Local variables:
# coding: utf-8
# mode: python
# End:
# vim: fileencoding=utf-8 filetype=python :
